swift LOG 输出

LOG 输出,一直用oc 的条件编译, 刚发现swift 同样可以实现
现记录
转: http://swifter.tips/log/
http://swifter.tips/condition-compile/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
LOG 输出
由 王巍 (@ONEVCAT) 发布于 2015-12-30
Log 输出是程序开发中很重要的组成部分,虽然它并不是直接的业务代码,但是却可以忠实地反映我们的程序是如何工作的,以及记录程序运行的过程中发生了什么。
Swift 中,最简单的输出方法就是使用 print,在我们关心的地方输出字符串和值。但是这并不够,试想一下当程序变得非常复杂的时候,我们可能会输出很多内容,而想在其中寻找到我们希望的输出其实并不容易。我们往往需要更好更精确的输出,这包括输出这个 log 的文件,调用的行号以及所处的方法名字等等。
我们当然可以在 print 的时候将当前的文件名字和那些必要的信息作为参数同我们的消息一起进行打印:
// Test.swift
func method() {
//...
print("文件名:Test.swift, 方法名:method,这是一条输出")
//...
}
但是这显然非常麻烦,每次输入文件名和方法名不说,随着代码的改变,这些 Log 的位置也可能发生改变,这时我们可能还需要不断地去维护这些输出,代价实在太大。
Swift 中,编译器为我们准备了几个很有用的编译符号,用来处理类似这样的需求,它们分别是:
符号 类型 描述
#file String 包含这个符号的文件的路径
#line Int 符号出现处的行号
#column Int 符号出现处的列
#function String 包含这个符号的方法名字
因此,我们可以通过使用这些符号来写一个好一些的 Log 输出方法:
func printLog<T>(message: T,
file: String = #file,
method: String = #function,
line: Int = #line)
{
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
}
这样,在进行 log 的时候我们只需要使用这个方法就能完成文件名,行号以及方法名的输出了。最棒的是,我们不再需要对这样的输出进行维护,无论在哪里它都能正确地输出各个参数:
// Test.swift
func method() {
//...
printLog("这是一条输出")
//...
}
// 输出:
// Test.swift[62], method(): 这是一条输出
另外,对于 log 输出更多地其实是用在程序开发和调试的过程中的,过多的输出有可能对运行的性能造成影响。在 Release 版本中关闭掉向控制台的输出也是软件开发中一种常见的做法。如果我们在开发中就注意使用了统一的 log 输出的话,这就变得非常简单了。使用条件编译的方法,我们可以添加条件,并设置合适的编译配置,使 printLog 的内容在 Release 时被去掉,从而成为一个空方法:
func printLog<T>(message: T,
file: String = #file,
method: String = #function,
line: Int = #line)
{
#if DEBUG
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
#endif
}
新版本的 LLVM 编译器在遇到这个空方法时,甚至会直接将这个方法整个去掉,完全不去调用它,从而实现零成本。

主要用到条件编译
上述需要设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
条件编译
由 王巍 (@ONEVCAT) 发布于 2014-12-02
在 C 系语言中,可以使用 #if 或者 #ifdef 之类的编译条件分支来控制哪些代码需要编译,而哪些代码不需要。Swift 中没有宏定义的概念,因此我们不能使用 #ifdef 的方法来检查某个符号是否经过宏定义。但是为了控制编译流程和内容,Swift 还是为我们提供了几种简单的机制来根据需求定制编译内容的。
首先是 #if 这一套编译标记还是存在的,使用的语法也和原来没有区别:
#if <condition>
#elseif <condition>
#else
#endif
当然,#elseif#else 是可选的。
但是这几个表达式里的 condition 并不是任意的。Swift 内建了几种平台和架构的组合,来帮助我们为不同的平台编译不同的代码,具体地:
方法 可选参数
os() OSX, iOS
arch() x86_64, arm, arm64, i386
注意这些方法和参数都是大小写敏感的。举个例子,如果我们统一我们在 iOS 平台和 Mac 平台的关于颜色的 API 的话,一种可能的方法就是配合 typealias 进行条件编译:
#if os(OSX)
typealias Color = NSColor
#else
typealias Color = UIColor
#endif
另外对于 arch() 的参数需要说明的是 arm 和 arm64 两项分别对应 32 位 CPU 和 64 位 CPU 的真机情况,而对于模拟器,相应地 32 位设备的模拟器和 64 位设备的模拟器所对应的分别是 i386 和 x86_64,它们也是需要分开对待的。
另一种方式是对自定义的符号进行条件编译,比如我们需要使用同一个 target 完成同一个 app 的收费版和免费版两个版本,并且希望在点击某个按钮时收费版本执行功能,而免费版本弹出提示的话,可以使用类似下面的方法:
@IBAction func someButtonPressed(sender: AnyObject!) {
#if FREE_VERSION
// 弹出购买提示,导航至商店等
#else
// 实际功能
#endif
}
在这里我们用 FREE_VERSION 这个编译符号来代表免费版本。为了使之有效,我们需要在项目的编译选项中进行设置,在项目的 Build Settings 中,找到 Swift Compiler - Custom Flags,并在其中的 Other Swift Flags 加上 -D FREE_VERSION 就可以了。

自己实现 :
设置
Switf Compiler - Custom Flags

中 (
active Compilation Conditions (不需要-D) 或者
Other swift flags (需要 -D 前缀)

1
2
3
4
#if DEBUG
// 逻辑
#endif

)debug 选项中加入 -D DEBUG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// 全局函数输入log 条件编译,正式版本不输出
///
/// - Parameters:
/// - message: <#message description#>
/// - file: <#file description#>
/// - method: <#method description#>
/// - line: <#line description#>
func printLog<T>(_ message: T,
file: String = #file,
method: String = #function,
line: Int = #line)
{
// 条件编译还不行 需要在项目中配置条件 Swift Compiler - Custom Flags 加上-D XXX ,debug条件下加
#if DEBUG
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
#endif
}
Author

陈昭

Posted on

2016-12-05

Updated on

2021-12-27

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Kommentare

You forgot to set the shortname for Disqus. Please set it in _config.yml.